# Active Record Migrations
Introduction
Migrations are like version control for your database, allowing your team to define and share the application's database schema definition. If you have ever had to tell a teammate to manually add a column to their local database schema after pulling in your changes from source control, you've faced the problem that database migrations solve.
Generating Migrations
You may use the
bin/rails generate migration
rails command to generate a database migration. The new migration will be placed in your **db/migrate
**directory. Each migration filename contains a timestamp that allows Laravel to determine the order of the migrations:bin/rails generate migration AddPartNumberToProducts
1This will create an appropriately named empty migration:
class AddPartNumberToProducts < ActiveRecord::Migration[7.0] def change end end
1
2
3
4Rials will use the name of the migration to attempt to guess the name of the table and whether or not the migration will be creating a new table. If Rails is able to determine the table name from the migration name, Rails will pre-fill the generated migration file with the specified table. Otherwise, you may simply specify the table in the migration file manually.
If the migration name is of the form "AddColumnToTable" or "RemoveColumnFromTable" and is followed by a list of column names and types then a migration containing the appropriate
[add_column](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-add_column)
and[remove_column](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-remove_column)
statements will be created.bin/rails generate migration AddPartNumberToProducts part_number:string
1will generate
class AddPartNumberToProducts < ActiveRecord::Migration[7.0] def change add_column :products, :part_number, :string end end
1
2
3
4
5Migration Structure
Sometimes we see the method name is
up
anddown
and sometimes we see the method name ischange
in the migration file.class AddPartNumberToProducts < ActiveRecord::Migration[7.0] def change end end
1
2
3
4You can also use the old style of migration using
up
anddown
methods instead of thechange
method. Theup
method should describe the transformation you'd like to make to your schema, and thedown
method of your migration should revert the transformations done by theup
method.class ExampleMigration < ActiveRecord::Migration[7.0] def up create_table :distributors do |t| t.string :zipcode end end def down drop_table :distributors end end
1
2
3
4
5
6
7
8
9
10
11Running Migration
To run all of your outstanding migrations, execute the
migrate
Artisan command:bin/rails db:migrate
1If you would like to see which migrations have run thus far, you may use the
migrate:status
rails command:bin/rails db:migrate:status
1If you specify a target version, Active Record will run the required migrations (change, up, down) until it has reached the specified version. The version is the numerical prefix on the migration's filename. For example, to migrate to version 20080906120000 run:
bin/rails db:migrate VERSION=20080906120000
1If version 20080906120000 is greater than the current version (i.e., it is migrating upwards), this will run the
change
(orup
) method on all migrations up to and including 20080906120000, and will not execute any later migrations. If migrating downwards, this will run thedown
method on all the migrations down to, but not including, 20080906120000.By default running
bin/rails db:migrate
will run in thedevelopment
environment. To run migrations against another environment you can specify it using theRAILS_ENV
environment variable while running the command. For example to run migrations against thetest
environment you could run:bin/rails db:migrate RAILS_ENV=test
1Rolling Back
bin/rails db:rollback bin/rails db:rollback STEP=3 #shortcut for doing a rollback and then migrating back up again. bin/rails db:migrate:redo STEP=3
1
2
3
4
5
6Table
Creating Table
create_table :products do |t| t.string :name end
1
2
3which creates a
products
table with a column calledname
.By default,
create_table
will create a primary key calledid
. You can change the name of the primary key with the:primary_key
option or, if you don't want a primary key at all, you can pass the optionid: false
. If you need to pass database specific options you can place an SQL fragment in the:options
option. For example:create_table :products, options: "ENGINE=BLACKHOLE" do |t| t.string :name, null: false end
1
2
3will append
ENGINE=BLACKHOLE
to the SQL statement used to create the table.An index can be created on the columns created within the
create_table
block by passing true or an options hash to the:index
option:create_table :users do |t| t.string :name, index: true t.string :email, index: { unique: true, name: 'unique_emails' } end
1
2
3
4Creating a Join Table
create_join_table :products, :categories
1which creates a
categories_products
table with two columns calledcategory_id
andproduct_id
. These columns have the option:null
set tofalse
by default. This can be overridden by specifying the:column_options
option:create_join_table :products, :categories, column_options: { null: true }
1By default, the name of the join table comes from the union of the first two arguments provided to create_join_table, in alphabetical order. To customize the name of the table, provide a
:table_name
option:create_join_table :products, :categories, table_name: :categorization
1creates a
categorization
table.create_join_table
also accepts a block, which you can use to add indices (which are not created by default) or additional columns:create_join_table :products, :categories do |t| t.index :product_id t.index :category_id end
1
2
3
4Changing Table
A close cousin of
create_table
is[change_table](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_table)
, used for changing existing tables. It is used in a similar fashion tocreate_table
but the object yielded to the block knows more tricks. For example:change_table :products do |t| t.remove :description, :name t.string :part_number t.index :part_number t.rename :upccode, :upc_code end
1
2
3
4
5
6removes the
description
andname
columns, creates apart_number
string column and adds an index on it. Finally it renames theupccode
column.Changing Columns
Like the
remove_column
andadd_column
Rails provides the[change_column](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_column)
migration method.change_column :products, :part_number, :text
1This changes the column
part_number
on products table to be a:text
field. Note thatchange_column
command is irreversible.Besides
change_column
,the[change_column_null](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_column_null)
and[change_column_default](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html#method-i-change_column_default)
method are used specifically to change a not null constraint and default values of a column.change_column_null :products, :name, false change_column_default :products, :approved, from: true, to: false
1
2This sets
:name
field on products to aNOT NULL
column and the default value of the:approved
field from true to false.References
The
add_reference
method allows the creation of an appropriately named column.add_reference :users, :role
1This migration will create a
role_id
column in the users table. It creates an index for this column as well, unless explicitly told not with theindex: false
option:add_reference :users, :role, index: false
1The method
add_belongs_to
is an alias ofadd_reference
.add_belongs_to :taggings, :taggable, polymorphic: true
1The polymorphic option will create two columns on the taggings table which can be used for polymorphic associations:
taggable_type
andtaggable_id
.A foreign key can be created with the
foreign_key
option.add_reference :users, :role, foreign_key: true
1References can also be removed:
remove_reference :products, :user, foreign_key: true, index: false
1When Helpers aren't Enough
If the helpers provided by Active Record aren't enough you can use the
[execute](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/ConnectionAdapters/DatabaseStatements.html#method-i-execute)
method to execute arbitrary SQL:Product.connection.execute("UPDATE products SET price = 'free' WHERE 1=1")
1